/*
 * LandscapeBuilder.java
 * 
 * Created on 2008-04-19, 17:13:24
 */

package planesimulator.terrain;

import java.io.IOException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.ImageIcon;

import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.math.Vector3f;
import com.jme.renderer.Renderer;
import com.jme.scene.state.TextureState;
import com.jme.system.DisplaySystem;
import com.jme.util.TextureManager;
import com.jmex.terrain.TerrainPage;
import com.jmex.terrain.util.ImageBasedHeightMap;
import com.jmex.terrain.util.ProceduralSplatTextureGenerator;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;

/**
 * Class which will be used to build terrain.
 * The main method is build which return instance of TerrainPage
 * 
 * @author Mateusz 'm4linka' Malinowski (m4linka@gmail.com)
 */
public class LandscapeBuilder {
  
  /** Heightmap size */
  protected static int size;
  
  /** Number of iterations */
  protected static int iterations;
  
  /** Min radius */
  protected static float min_radius;
  
  /** Max radius */
  protected static float max_radius;
  
  /** The size of leaf nodes (used in TerrainPage) */
  protected static int block_size;
  
  /** Terrain scale vector (used in TerrainPage) */
  protected static Vector3f terrain_scale;

  /** True if level of detail is set */
  protected static boolean clod;
  
  /** Texture path */
  private final static String TEXTURE_PREFIX = "planesimulator/data/terrain/";
  
  /** Texture size */
  private final static int TEXTURE_SIZE =  256;
  
  /** Textures */
  private final static TerrainTexture TEXTURES[] = 
  {
    new TerrainTexture("grassb.png", -128, 0, 128),
    new TerrainTexture("dirt.jpg", 0, 128, 256),
    new TerrainTexture("highest.jpg", 128, 255, 350),
  };
  
  /** Detail texture */
  private final static String TEXTURE_DETAIL =
      TEXTURE_PREFIX + "detail.jpg";
  
  /** The heightmap resource **/
  private final static String HEIGHT_MAP_RESOURCE =
      "planesimulator/data/terrain/hm3l.jpg";
  
  
  /**
   * Class that helps with texture'a management.
   */
  private static class TerrainTexture {
    private final transient String texture_name;
    private final transient int low;
    private final transient int optimal;
    private final transient int high;
    
    public TerrainTexture(final String texture_name, final int low,
        final int optimal, final int high) {
      this.texture_name = texture_name;
      this.low = low;
      this.optimal = optimal;
      this.high = high;
    }
  }

  
  /**
   * Sets default settings.
   */
  protected final static void defaultSettings() {
    size = 513;
    iterations = 50000;
    min_radius = 2.0f;
    max_radius = 50.0f;
    
    block_size = 33;
//    terrain_scale = new Vector3f(10.0f, 3.0f, 10.0f);
    terrain_scale = new Vector3f(45.0f, 19.0f, 45.0f);
    clod = false;
  }
  
  
  /**
   * 
   * @param display system
   * @return terrain
   */
  public final static TerrainPage build(final DisplaySystem display) {
    defaultSettings();
    
    // prepare the heightmap;
    BufferedImage image = null;
    try {
      final URL resource_name = Thread.currentThread().getContextClassLoader()
          .getResource(HEIGHT_MAP_RESOURCE);
      image = ImageIO.read(resource_name);
    } catch (IOException ex) {
      Logger.getLogger(LandscapeBuilder.class.getName())
          .log(Level.SEVERE, null, ex);
    }
    
    final ImageBasedHeightMap height_map =
        new ImageBasedHeightMap(image);
    height_map.setHeightScale(0.25f);
    final TerrainPage terrain_page = new TerrainPage("Terrain",
        block_size, height_map.getSize(), terrain_scale, 
        height_map.getHeightMap(), clod);
    terrain_page.setModelBound(new BoundingBox());
    terrain_page.updateModelBound();
    
    // load textures
    final ProceduralSplatTextureGenerator pst =
        new ProceduralSplatTextureGenerator(height_map);
    
    URL resource;
    for(TerrainTexture terrain_texture : TEXTURES) {
      resource = Thread.currentThread().getContextClassLoader().getResource(
          TEXTURE_PREFIX + terrain_texture.texture_name);
      pst.addTexture(new ImageIcon(resource), terrain_texture.low,
          terrain_texture.optimal, terrain_texture.high);
      
    }
    pst.createTexture(TEXTURE_SIZE);
    
    // assign textures to the terrain
    final TextureState texture_state = display.getRenderer().createTextureState();
    
    // normal texture
    final Texture texture1 = TextureManager.loadTexture(
        pst.getImageIcon().getImage(),Texture.MM_LINEAR_LINEAR, 
        Texture.FM_LINEAR, true);
    texture_state.setTexture(texture1,0);
    
    // adds details
    final Texture texture2 = TextureManager.loadTexture(
        Thread.currentThread().getContextClassLoader()
            .getResource(TEXTURE_DETAIL),
        Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR);
    texture2.setWrap(Texture.WM_WRAP_S_WRAP_T);
    texture_state.setTexture(texture2,1);
    
    terrain_page.setRenderState(texture_state);
    terrain_page.setDetailTexture(1, 16);
    terrain_page.setRenderQueueMode(Renderer.QUEUE_OPAQUE);
    
    return terrain_page;
  }
  
}
